package com.nx.platform.es.biz.esspider.expression;

import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

/**
 * @author
 * @date 2018/05/22
 */
public class ValExpression {

    private final ParseTree tree;

    private ValExpression(@Nullable ParseTree tree) {
        this.tree = tree;
    }

    public static ValExpression create(@Nullable String expression)
            throws IllegalArgumentException {
        AtomicReference<String> errorMsg = new AtomicReference<>();
        ParseTree tree = parse(expression, errorMsg::set).orElseThrow(
                () -> new IllegalArgumentException(errorMsg.get()));
        return new ValExpression(tree);
    }

    public static ValExpression create(@Nullable String expression,
            @NotNull Consumer<String> errorMsgConsumer) {
        ParseTree tree = parse(expression, errorMsgConsumer).orElse(null);
        return new ValExpression(tree);
    }

    private static Optional<ParseTree> parse(@Nullable String expression,
            @NotNull Consumer<String> errorMsgConsumer) {
        if (expression == null) {
            errorMsgConsumer.accept("expression null");
            return Optional.empty();
        }
        expression = expression.trim();
        if (expression.isEmpty()) {
            errorMsgConsumer.accept("expression empty");
            return Optional.empty();
        }
        try {
            CharStream input = CharStreams.fromString(expression);
            ExpressionLexer lexer = new ExpressionLexer(input);
            CommonTokenStream tokens = new CommonTokenStream(lexer);
            ExpressionParser parser = new ExpressionParser(tokens);
            parser.removeErrorListeners();
            parser.addErrorListener(new ExpressionErrorListener(errorMsgConsumer));
            return Optional.ofNullable(parser.val());
        } catch (RuntimeException e) {
            errorMsgConsumer.accept(e.getMessage());
            return Optional.empty();
        }
    }

    private static Long eval(@Nullable ParseTree tree,
            @Nullable Map<String, Object> params) {
        if (tree == null) {
            return null;
        }
        try {
            return (Long) new ExpressionVisitorImpl(params).visit(tree);
        } catch (RuntimeException e) {
            return null;
        }
    }

    public Long eval(@Nullable Map<String, Object> params) {
        return eval(tree, params);
    }

}
